package run.yiqi.blog.controller;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import run.yiqi.blog.dao.IssueDao;
import run.yiqi.blog.dao.TagOptionDao;
import run.yiqi.blog.dao.TagTypeDao;
import run.yiqi.blog.model.TagOption;
import run.yiqi.blog.model.TagType;
import run.yiqi.blog.vo.TagOptionVo;
import run.yiqi.blog.vo.TagTypeVo;
import run.yiqi.util.Result;
import run.yiqi.util.StringUtil;

@Slf4j
@Controller
public class TagController {

    @Lazy
    @Autowired
    private IssueDao blogDao;
    
    @Lazy
    @Autowired
    private TagTypeDao tagTypeDao;
    
    @Lazy
    @Autowired
    private TagOptionDao tagOptionDao;

    @ResponseBody
    @RequestMapping(value = { "/api/admin/listtags" }, method = RequestMethod.POST)
    public List<TagTypeVo> listTags() {
        log.info("/api/admin/listtags");

        List<TagTypeVo> result = new ArrayList<TagTypeVo>();

        List<TagType> tagtypes = tagTypeDao.findAll(new Specification<TagType>() {
            private static final long serialVersionUID = 1L;

            @Override
            public Predicate toPredicate(Root<TagType> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                root.join("options", JoinType.LEFT);//.join("issues", JoinType.LEFT);

                query.distinct(true);
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        }, new Sort(Direction.ASC, "sortOrder"));

        tagtypes = tagtypes.stream().distinct().collect(Collectors.toList());
        tagtypes.forEach(tagtype -> {
            TagTypeVo tagtypevo = result.stream().filter(t->t.getTypeid().equals(tagtype.getTypeid())).findFirst().orElse(new TagTypeVo());
            tagtypevo.setTypeid(tagtype.getTypeid());
            tagtypevo.setTypename(tagtype.getTypename());
            tagtypevo.setSortOrder(tagtype.getSortOrder());
            
            tagtype.getOptions().forEach(tagoption->{
                TagOptionVo tagoptionvo = tagtypevo.getOptions().stream().filter(t->t.getOptionid().equals(tagoption.getOptionid())).findFirst().orElse(new TagOptionVo());
                tagoptionvo.setOptionid(tagoption.getOptionid());
                tagoptionvo.setOptionname(tagoption.getOptionname());
                //tagoptionvo.setEntitycount(tagoption.getIssues().size());
                tagoptionvo.setSortOrder(tagoption.getSortOrder());
                
                if(!tagtypevo.getOptions().contains(tagoptionvo)) {
                    tagtypevo.getOptions().add(tagoptionvo);
                }
            });
            tagtypevo.getOptions().sort(Comparator.comparing(TagOptionVo::getSortOrder));
            
            if(!result.contains(tagtypevo)) {
                result.add(tagtypevo);
            }
        });
        result.sort(Comparator.comparing(TagTypeVo::getTypeid));

        return result;
    }

    @ResponseBody
    @RequestMapping(value = "/api/admin/savetagtype", method = RequestMethod.POST)
    public void saveTagType(@RequestBody TagTypeVo tagTypeVo) {
        log.info("/api/admin/savetagtype");

        TagType tagType = null;
        if(StringUtil.isEmpty(tagTypeVo.getTypeid())) {
            tagType = new TagType();
            //tagType.setTypeid(UUID.randomUUID().toString());
            tagType.setTypename(tagTypeVo.getTypename());
            tagType.setSortOrder(tagTypeVo.getSortOrder());
        } else {
            tagType = this.tagTypeDao.findById(tagTypeVo.getTypeid()).get();
            tagType.setTypename(tagTypeVo.getTypename());
            tagType.setSortOrder(tagTypeVo.getSortOrder());
        }
        this.tagTypeDao.save(tagType);
    }

    @ResponseBody
    @RequestMapping(value = "/api/admin/savetagoption", method = RequestMethod.POST)
    public void saveTagOption(@RequestBody TagOptionVo tagOptionVo) {
        log.info("/api/admin/savetagoption: "+tagOptionVo.toString());

        TagOption tagOption = null;
        if(StringUtil.isEmpty(tagOptionVo.getOptionid())) {
            tagOption = new TagOption();
            //tagOption.setOptionid(UUID.randomUUID().toString());
            tagOption.setOptionname(tagOptionVo.getOptionname());
            
            tagOption.setTagtype(this.tagTypeDao.findById(tagOptionVo.getTypeid()).get());
            tagOption.setSortOrder(tagOptionVo.getSortOrder());
            
            tagOption.getTagtype().getOptions().add(tagOption);
        } else {
            tagOption = this.tagOptionDao.findById(tagOptionVo.getOptionid()).get();
            tagOption.setOptionname(tagOptionVo.getOptionname());
            tagOption.setTagtype(this.tagTypeDao.findById(tagOptionVo.getTypeid()).get());
            tagOption.setSortOrder(tagOptionVo.getSortOrder());
            
            //tagOption.getTagtype().getOptions().add(tagOption);
        }
        this.tagOptionDao.save(tagOption);
    }

    @Transactional
    @SneakyThrows
    @ResponseBody
    @RequestMapping(value = "/api/admin/deletetagtype", method = RequestMethod.POST)
    public Result deleteTagType(@RequestBody TagTypeVo tagTypeVo) {
        log.info("deleteTagType..."+tagTypeVo.getTypeid());
        Result result = new Result();
        
        TagType tagType = this.tagTypeDao.findById(tagTypeVo.getTypeid()).get();
        tagType.getOptions().forEach(option->option.getIssues().forEach(blog->blog.getTags().remove(option)));
        
        this.tagTypeDao.delete(tagType);
        
        result.setResult(true);
        result.setDescription("");
        return result;
    }

    @Transactional
    @SneakyThrows
    @ResponseBody
    @RequestMapping(value = "/api/admin/deletetagoption", method = RequestMethod.POST)
    public Result deleteTagOption(@RequestBody TagOptionVo tagOptionVo) {
        log.info("deleteTagOption..."+tagOptionVo.getOptionid());
        Result result = new Result();
        
        TagOption tagOption = this.tagOptionDao.findById(tagOptionVo.getOptionid()).get();
        tagOption.getIssues().forEach(blog->blog.getTags().remove(tagOption));
        this.tagOptionDao.delete(tagOption);
        
        result.setResult(true);
        result.setDescription("");
        return result;
    }
    
}